import os
from datetime import datetime
import sqlalchemy
import uuid
from vulcanus.database.proxy import MysqlProxy
from vulcanus.database.helper import sort_and_page
from vulcanus.log.log import LOGGER
from vulcanus.restful.resp.state import (
    DATA_DEPENDENCY_ERROR,
    DATA_EXIST,
    DATABASE_UPDATE_ERROR,
    DATABASE_DELETE_ERROR,
    DATABASE_INSERT_ERROR,
    DATABASE_QUERY_ERROR,
    NO_DATA,
    PARAM_ERROR,
    SUCCEED,
)
from zeus.operation_service.app.core.asset import ASSET_PATH
from zeus.operation_service.app.core.file_util import FileUtil
from zeus.operation_service.app.serialize.asset import GetAssetPage_ResponseSchema
from zeus.operation_service.database import Asset, TaskAssetItem, TaskAssetItemTask, Task, HealthCheckResultItem


class AssetProxy(MysqlProxy):
    def __init__(self):
        super().__init__()
        if not self.session:
            self.connect()

    def get_assets(self, asset_page_filter):
        result = {}
        try:
            result = self._query_assets_page(asset_page_filter)
            LOGGER.info("Query assets succeed")
            return SUCCEED, result
        except sqlalchemy.exc.SQLAlchemyError as error:
            LOGGER.error(error)
            LOGGER.error("Query assets fail")
            return DATABASE_QUERY_ERROR, result

    def add_asset(self, data):
        try:
            self.session.add(Asset(**data, asset_id=str(uuid.uuid1()), update_time=datetime.now()))
            self.session.commit()
            LOGGER.info("add asset succeed")
            return SUCCEED
        except sqlalchemy.exc.SQLAlchemyError as error:
            LOGGER.error(error)
            self.session.rollback()
            LOGGER.error("add asset fail")
            return DATABASE_INSERT_ERROR

    def batch_delete_asset(self, asset_ids):
        delete_success_ids = list()
        delete_failed_ids = list()
        for asset_id in asset_ids:
            try:
                asset = self.session.query(Asset).filter(Asset.asset_id == asset_id).first()
                if not asset:
                    delete_success_ids.append(asset_id)
                    continue
                self.session.delete(asset)
                self.delete_asset_associations(asset_id)
                self.session.commit()
                FileUtil.dir_remove(os.path.join(ASSET_PATH, asset.asset_dir))
                LOGGER.info(f"asset {asset_id} delete succeed ")
            except sqlalchemy.exc.SQLAlchemyError as error:
                LOGGER.error(error)
                LOGGER.error(f"delete asset {asset_id} fail")
                self.session.rollback()
                delete_failed_ids.append(asset_id)
                continue
            delete_success_ids.append(asset_id)

        if len(delete_success_ids) == len(asset_ids):
            return SUCCEED, {}
        else:
            return DATABASE_DELETE_ERROR, delete_failed_ids

    def delete_asset_associations(self, asset_id):
        asset_item_associations = self.session.query(TaskAssetItem).filter(TaskAssetItem.asset_id == asset_id).all()
        for asset_item in asset_item_associations:
            item_task_associations = (
                self.session.query(Task.task_id)
                .join(TaskAssetItemTask, Task.task_id == TaskAssetItemTask.task_id)
                .filter(TaskAssetItemTask.item_id == asset_item.item_id)
            )
            total_count = len(item_task_associations.all())
            if not total_count:
                self.session.delete(asset_item)

    def get_all_asset(self):
        return self.session.query(Asset).all()

    def get_asset_by_asset_id(self, asset_id):
        return self.session.query(Asset).filter(Asset.asset_id == asset_id).first()

    def get_asset_by_asset_dir(self, asset_dir):
        return self.session.query(Asset).filter(Asset.asset_dir == asset_dir).first()

    def del_asset_by_asset_dir(self, asset_dir):
        self.session.query(Asset).filter(Asset.asset_dir == asset_dir).delete()
        self.session.commit()

    def add_asset_item_batch(self, items_info: list) -> str:
        try:
            self.session.bulk_save_objects(items_info)
            self.session.commit()
            LOGGER.info(f"add TaskAssetItem succeed")
            return SUCCEED
        except sqlalchemy.exc.SQLAlchemyError as error:
            LOGGER.error(error)
            self.session.rollback()
            return DATABASE_INSERT_ERROR

    def add_health_check_result_item(self, task_id, host_id, asset_item_id, task_result):
        try:
            item = self.session.query(HealthCheckResultItem) \
                .filter(
                    HealthCheckResultItem.task_id == task_id,
                    HealthCheckResultItem.host_id == host_id,
                    HealthCheckResultItem.health_check_asset_item_id == asset_item_id
                ).first()
            if item is None:
                item = HealthCheckResultItem(result_id=str(uuid.uuid1()), task_id=task_id, host_id=host_id, health_check_asset_item_id=asset_item_id, task_result=task_result)
                self.session.add(item)
            else:
                item.task_result = task_result
            self.session.commit()
            return SUCCEED
        except sqlalchemy.exc.SQLAlchemyError as error:
            LOGGER.error(error)
            self.session.rollback()
        return DATABASE_INSERT_ERROR

    def get_asset_item(self, asset_id, item_type):
        return self.session.query(TaskAssetItem).filter(TaskAssetItem.asset_id == asset_id, TaskAssetItem.item_type == item_type).all()

    def get_asset_item_by_id(self, item_id):
        return self.session.query(TaskAssetItem).filter(TaskAssetItem.item_id == item_id).first()

    def get_asset_item_by_item_path(self, asset_id, item_path):
        return self.session.query(TaskAssetItem).filter(TaskAssetItem.asset_id == asset_id, TaskAssetItem.item_path == item_path).first()

    def get_filtered_asset_item(self, task_id, task_type):
        asset_ids = []
        asset_items = (
            self.session.query(TaskAssetItem)
            .join(TaskAssetItemTask, TaskAssetItem.item_id == TaskAssetItemTask.item_id)
            .filter(TaskAssetItemTask.task_id == task_id, TaskAssetItem.item_type == task_type)
            .distinct()
            .all()
        )
        if asset_items:
            asset_ids = list(set([asset_item.asset_id for asset_item in asset_items]))
        return asset_ids, asset_items

    def get_result_item_by_task_id(self, task_id):
        return self.session.query(HealthCheckResultItem).filter(HealthCheckResultItem.task_id == task_id)

    def get_health_check_result_item(self, task_id, host_id):
        return self.session.query(HealthCheckResultItem).filter(HealthCheckResultItem.task_id == task_id, HealthCheckResultItem.host_id == host_id)

    def get_result_item_by_id(self, task_id, host_id, item_id):
        return self.session.query(HealthCheckResultItem).filter(HealthCheckResultItem.task_id == task_id, HealthCheckResultItem.host_id == host_id, HealthCheckResultItem.health_check_asset_item_id == item_id)

    def del_health_check_result_item(self, result_id):
        self.session.query(HealthCheckResultItem).filter(HealthCheckResultItem.result_id == result_id).delete()
        self.session.commit()

    @staticmethod
    def _get_asset_column(column_name):
        if not column_name:
            return None
        return getattr(Asset, column_name)
    
    def _query_assets_page(self, page_filter):
        result = {"total_count": 0, "total_page": 0, "asset_infos": []}
        task_type = page_filter.get("task_type", "all")
        if task_type == 'all':
            assets_query = self.session.query(Asset)
        else:
            assets_query = self.session.query(Asset).filter(Asset.support_type.contains(task_type))

        result["total_count"] = assets_query.count()
        if not result["total_count"]:
            return result
        sort_column = self._get_asset_column(page_filter.get("sort"))
        processed_query, total_page = sort_and_page(
            assets_query, sort_column, page_filter.get("direction"), page_filter.get("per_page"), page_filter.get("page")
        )
        result['total_page'] = total_page
        result['asset_infos'] = GetAssetPage_ResponseSchema(many=True).dump(processed_query.all())
        return result
